package 多线程;

//在java中，想要让一个线程停下来，有三种办法：
//(1)采用退出标志，使得run方法执行完之后线程自然终止。
//(2)使用stop强行终止线程，但该方法由于安全问题已经被deprecated。
//(3)使用中断机制。
//
//第一种方法没特别之处，无非是在覆盖Runnable接口之时对run方法中添加状态标识逻辑。比如：
//
//public class MyThread extends Thread
//{
//    private boolean running;
//    @Override
//    public run(){
//         if(running){
//             //....business methods
//         }
//    }
//}

//下面详细讨论中断机制。
//这里的中断是软中断，即在某个线程中可以将目标线程的中断标志位置位，然后在目标线程进行中断检查的时候来判断自己应当如何响应中断。
//特别的，可以以此来停止目标线程。中断的局限性，无法对出于死锁状态的线程起作用。
//
//简单原理：
//java.lang.Thread类中提供了一个方法来将线程的中断标志位置位：
//Thread.interrupt()方法，其函数申明为：public void interrupt();该方法会将线程的中断标志位置位。
//java.lang.Thread类还两个方法来检查中断标志：

//public static boolean interrupted(){
//    return currentThread().isInterrupted(true);
//}
//
//public boolean isInterrupted(){
//    return isInterrupted(false);
//}
//
//这两个方法分别调用同一个原生方法来检查中断状态并有所不同。
//一方面interrupted()是静态方法，可以使用Thread.interrupted()的方法来调用。
//而isInterrupted() 则需要一个实例对象作为隐式参数，使用threadInstance.isInterrupted()来调用。
//另一方面，interrupted在调用后会立即将线程中断标志位复位，而isInterrupted则不会这么做。

//非阻塞线程的中断问题：
//调用Thread.interrupt将目标线程中断标志位置位，然后在目标线程中检查中断标志位。
//如果检查到中断之后使用throw new InterruptedException()抛出一个异常，然后使用catch语句来捕获这个异常，则可以在这里完成一些中断逻辑。
//特别的，可以使用这种方法退出线程。下面是这种退出方式的举例。

/*class UnblockingInterrupt extends Thread{
    @Override
    public void run(){
        //super.run();
        try{
            for(int i=0;i<500;i++) {
            	System.out.println(this.getName()+" "+this.getState()+" "+this.isInterrupted());
                //if(Thread.interrupted()){ //立即将线程中断标志位复位为：false
                if(this.isInterrupted()){ //线程中断标志位不变为：true
                    System.out.println("I'm interrupted and I'm going out");
                    throw new InterruptedException();
                }
                System.out.println("i="+(i+1));
            }
            System.out.println("I'm below the for clauses and I have no chance to run here");
        }catch(InterruptedException e){
        	//interrupted()调用，中断标志位置位:由true变为false，如果isInterrupted()调用，则中断标志位不会变化。
        	System.out.println(this.getName()+"0000 "+this.getState()+" "+Thread.interrupted());//interrupted()调用，中断标志位置位:false
            System.out.println("I'm in the catch clauses of the method UnblockingInterrupt.run()"+this.isInterrupted());
            e.printStackTrace();
            this.interrupt();//重设中断状态,避免吞掉中断状态会导致方法调用栈的上层得不到这些信息
            System.out.println(this.isInterrupted());//true
        }
        System.out.println("ttttttt");
    }
}

public class 线程中断 {
    public static void main(String[] args) throws InterruptedException{
        try{
            UnblockingInterrupt thread=new UnblockingInterrupt();
            thread.start();
            System.out.println(Thread.currentThread());
            Thread.sleep(1);//main线程休眠 1 millis，thread线程仍在运行,即0-500之间循环
            thread.interrupt();//main线程休眠结束，线程thread的中断标志位置位:true,
                               //thread线程run方法中Thread.interrupted()和thread.isInterrupted都为true,抛出
                               //InterruptedException,并捕获及打印栈轨迹 e.printStackTrace()
            System.out.println(Thread.currentThread().getName()+" "+Thread.currentThread().getState()+" "+Thread.interrupted());
        }catch(InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("mmmmmmmmmmm");
        //System.out.println(Thread.currentThread().getName()+" "+Thread.currentThread().getState()+" "+Thread.currentThread().isInterrupted());
    }
}*/

//输出结果(关键部分)为：
//
//...
//i=61
//Thread-0 RUNNABLE false
//i=62
//Thread-0 RUNNABLE false
//i=63
//Thread-0 RUNNABLE true
//I'm interrupted and I'm going out
//main RUNNABLE false
//Thread-00000 RUNNABLE true
//I'm in the catch clauses of the method UnblockingInterrupt.run()false
//mmmmmmmmmmm
//java.lang.InterruptedException
//ttttttt
//	at mytest/多线程.UnblockingInterrupt.run(线程中断.java:58)
//
//可以看到，当线程thread的中断标志位被main线程中使用thread.interrupt置位后，在执行thread线程的for循环的if条件检查时，
//Thread.interrupted()返回true随后在其中抛出一个InterruptedException异常，再使用catch子句捕获该异常，由于run方法中
//在catch子句之后再无语句，
//故线程从run方法返回，thread线程就此终止。
//
//阻塞线程的中断问题。
//首先，一个线程出于阻塞中断状态(实际上还包括等待状态）的原因，常见的有线程调用了thread.sleep、
//thread.join、thread.wait、1.5中的condition.await、以及可中断的通道上的 I/O 操作方法等。
//如果一个线程处于阻塞状态，若将其中断标志位置为，则会在产生阻塞的语句出抛出一个InterruptedException异常，
//并且在抛出异常后立即将线程的中断标示位复位，即重新设置为false。
//抛出异常是为了线程从阻塞状态醒过来，并在结束线程前让程序员有足够的时间来处理中断请求。
//换言之，中断将一个处于阻塞状态的线程的阻塞状态解除。
//注意，synchronized在获锁的过程中是不能被中断的，意思是说如果产生了死锁，则不可能被中断（请参考后面的测试例子）。
//与synchronized功能相似的reentrantLock.lock()方法也是一样，它也不可中断的。
//即如果发生死锁，那么reentrantLock.lock()方法无法终止，如果调用时被阻塞，则它一直阻塞到它获取到锁为止。
//但是如果调用带超时的tryLock方法reentrantLock.tryLock(long timeout, TimeUnit unit)，那么如果线程在等待时被中断，
//将抛出一个InterruptedException异常，这是一个非常有用的特性，因为它允许程序打破死锁。
//你也可以调用reentrantLock.lockInterruptibly()方法，它就相当于一个超时设为无限的tryLock方法。
//没有任何语言方面的需求一个被中断的线程应该终止。中断一个线程只是为了引起该线程的注意，被中断线程可以决定如何应对中断。
//某些线程非常重要，以至于它们应该不理会中断，而是在处理完抛出的异常之后继续执行，但是更普遍的情况是，一个线程将把中断看作一个终止请求。
//这种线程的run方法遵循如下形式：
//
//public void run() {
//    try {
//        ...
//        /*
//         * 不管循环里是否调用过线程阻塞的方法如sleep、join、wait，这里还是需要加上
//         * !Thread.currentThread().isInterrupted()条件，虽然抛出异常后退出了循环，显
//         * 得用阻塞的情况下是多余的，但如果调用了阻塞方法但没有阻塞时，这样会更安全、更及时。
//         */
//        while (!Thread.currentThread().isInterrupted()&& more work to do) {
//            do more work 
//        }
//    } catch (InterruptedException e) {
//        //线程在wait或sleep期间被中断了
//    } finally {
//        //线程结束前做一些清理工作
//    }
//}
//
//在这里，应当注意一个原则：尽量不要在底层代码中捕获InterruptedException原则。因为该异常一旦被捕获，线程的中断标志位就会被置位。
//对于底层代码而言，如果代码会抛出不知道应当如何应对的InterruptException异常，可以有下面两个选择。
//(1)捕获到InterruptedException异常后将中断标志位置位，让外层代码根据检查中断标志位来判断是否中断。
//
//public subTask(){
//    try{
//        //...the works may throw a InterruptedException
//    }catch(InterruptedException e){
//        Thread.currentThread.interrupt();
//    }
//}
//
//(2)更推荐的方法是，底层代码不捕获InterruptedException，直接将其抛给外层代码去解决
//
//public subTask() throws InterruptedException
//{
//    //...
//}
//
//中断失效的情况之临界区：
//进入临界区的代码是不允许中断的。这一点很好理解，临界区是并行问题为了保护临界资源的互斥访问而特地加锁的，
//一旦可以中断，那么锁的存在也就没有任何意义了。特别的，形成了死锁的线程会分别处于阻塞状态，但是它们都无法被中断。
//常见的有进入synchronized块的代码以及Lock.lock()之后没能得到锁而处于阻塞的状态。通常可以使用Lock.lockInterruptibly()来代
//替Lock.lock()，
//因为Lock.lockInterruptibly()可以接受中断，这个中断指的是，既然没能得到锁进入临界区，与其阻塞不如做些别的什么事。
//一旦进入临界区，任何方法都不得中断。
//
//中断失效的情况之不可中断I/O:
//自java 1.4之后对大量数据的I/O常用通道(channels)机制，它是可以被中断的I/O，也就是说如果这种I/O处于阻塞状态，可以使用中断来解除阻塞。
//但是存在着一些不可中断的操作，比如ServerSocket.accept()，inputSteam.read()等调用interrupt()对于这几个问题无效，因为它们都
//不抛出中断异常。如果拿不到资源，它们会无限期阻塞下去。
//对于inputStream等资源，有些(实现了interruptibleChannel接口)可以通过close()方法将资源关闭，对应的阻塞也会被放开。
//对于处理大型I/O时，推荐使用Channels


//《中断的原理》
//Java 中断机制是一种协作机制，也就是说通过中断并不能直接终止另一个线程，而需要被中断的线程自己处理中断。
//
//Java 中断模型也是这么简单，每个线程对象里都有一个 boolean 类型的标识，代表着是否有中断请求（该请求可以来自所有线程，包括被中断的线程本身）。
//例如，当线程 t1 想中断线程 t2，只需要在线程 t1 中将线程 t2 对象的中断标识置为 true，然后线程 t2 可以选择在合适的时候处理该中断请求，
//甚至可以不理会该请求，就像这个线程没有被中断一样。
//
//java.lang.Thread 类提供了几个方法来操作这个中断状态，这些方法包括：
//
//1.public static boolean interrupted
//测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说，如果连续两次调用该方法，则第二次调用将返回 false（在第一次调用已清除了其中断
//状态之后，且第二次调用检验完中断状态前，当前线程再次中断的情况除外）。
//
//2.public boolean isInterrupted()
//测试线程是否已经中断。线程的中断状态不受该方法的影响。
//
//3.public void interrupt()
//中断线程，将中断状态设置为true。
//
//中断及如何响应中断？
//
//假设某个线程要不停地处理某件事情(比如 i 一直自增)，但是还有个要求：在处理事情前，先要检查下这个线程是否被中断，如果已经被中断，处理就应该结束。
//
//下面是一些例子：

//public class 线程中断 {
// 
//    public static void main(String[] args) {
//        try {
//            MyThread thread = new MyThread();
//            thread.start();
//            Thread.sleep(20);//modify 2000 to 20
//            thread.interrupt();//请求中断MyThread线程
//        } catch (InterruptedException e) {
//            System.out.println("main catch");
//            e.printStackTrace();
//        }
//        System.out.println("end!");
//    }
//}
////main线程睡眠20ms后，执行第218行中断MyThread线程。
//
//class MyThread extends Thread {
//    @Override
//    public void run() {
//        super.run();
//        for (int i = 0; i < 500000; i++) {
//            if (this.interrupted()) {
//                System.out.println("should be stopped and exit");
//                break;
//            }
//            System.out.println("i=" + (i + 1));
//        }
//        System.out.println("this line is also executed. thread does not stopped");//尽管线程被中断,但并没有结束运行。这行代码还是会被执行
//    }
//}
//当MyThread获得CPU执行时，第233行的 if 测试中，检测到中断标识被设置。即MyThread线程检测到了main线程想要中断它的 请求。
//大多数情况下，MyThread检测到了中断请求，对该中断的响应是：退出执行（或者说是结束执行）。
//但是，上面第232至235行for循环，是执行break语句跳出for循环。但是，线程并没有结束，它只是跳出了for循环而已，它还会继续执行第239行的代码....
//因此，我们的问题是，当收到了中断请求后，如何结束该线程呢？
//一种可行的方法是使用 return 语句 而不是 break语句。。。。。哈哈。。。
//
//当然，一种更优雅的方式则是：抛出InterruptedException异常。
//
//看下面MyThread类的代码：
//
//public class MyThread extends Thread {
//    @Override
//    public void run() {
//        super.run();
//        try{
//            for (int i = 0; i < 500000; i++) {
//                if (this.interrupted()) {
//                    System.out.println("should be stopped and exit");
//                    throw new InterruptedException();
//                }
//                System.out.println("i=" + (i + 1));
//            }
//            System.out.println("this line cannot be executed. cause thread throws exception");//这行语句不会被执行!!!
//        }catch(InterruptedException e){
//            System.out.println("catch interrupted exception");
//            e.printStackTrace();
//        }
//    }
//}
//当MyThread线程检测到中断标识为true后，在第9行抛出InterruptedException异常。这样，该线程就不能再执行其他的正常语句了
//(如，第13行语句不会执行)。这里表明：interrupt()方法有两个作用，一个是将线程的中断状态置位(中断状态由false变成true)；
//另一个则是：让被中断的线程抛出InterruptedException异常。
//
//这是很重要的。这样，对于那些阻塞方法(比如 wait() 和 sleep())而言，当另一个线程调用interrupt()中断该线程时，该线程会从阻塞状态退出
//并且抛出中断异常。这样，我们就可以捕捉到中断异常，并根据实际情况对该线程从阻塞方法中异常退出而进行一些处理。
//
//比如说：线程A获得了锁进入了同步代码块中，但由于条件不足调用 wait() 方法阻塞了。这个时候，线程B执行 threadA.interrupt()请求中断线
//程A，此时线程A就会抛出InterruptedException，我们就可以在catch中捕获到这个异常并进行相应处理(比如进一步往上抛出)
//
//因此，上面就是一个采用抛出异常的方式来结束线程的示例。尽管该示例的实用性不大。原因在 IBM的这篇博文中：我们生吞了中断。
//
//在第14行，我们直接catch了异常，然后打印输出了一下而已，调用栈中的更高层的代码是无法获得关于该异常的信息的。
//
//第16行的e.printStackTrace()作用就相当于
//
//“（仅仅记录 InterruptedException 也不是明智的做法，因为等到人来读取日志的时候，再来对它作出处理就为时已晚了。）”---摘自参考博文
//
//上面我们是在run()方法中抛出异常，符合这里描述的：
//
//有时候抛出 InterruptedException 并不合适，例如当由 Runnable 定义的任务调用一个
//可中断的方法时，就是如此。在这种情况下，不能重新抛出 InterruptedException，但是
//您也不想什么都不做。当一个阻塞方法检测到中断并抛出 InterruptedException 时，它
//清除中断状态。如果捕捉到 InterruptedException 但是不能重新抛出它，那么应该保留
//中断发生的证据，以便调用栈中更高层的代码能知道中断，并对中断作出响应。该任务可以
//通过调用 interrupt() 以 “重新中断” 当前线程来完成，如清单 3 所示。 -----“摘自参考博文”
//
//因为，run方法是实现的Runnable接口中的方法。不能像下面这样定义，也即上面所说的：“不能重新抛出InterruptedException”。
//
//        @Override
//        public void run() throws InterruptedException{//这是错误的
//          //do something...
//因此，一个更好的解决方案是：调用 interrupt() 以 “重新中断” 当前线程。改进MyThread类中catch异常的方式，如下：
//
//public class MyThread extends Thread {
//    @Override
//    public void run() {
//        super.run();
//        try{
//            for (int i = 0; i < 500000; i++) {
//                if (this.interrupted()) {
//                    System.out.println("should be stopped and exit");
//                    throw new InterruptedException();
//                }
//                System.out.println("i=" + (i + 1));
//            }
//            System.out.println("this line cannot be executed. cause thread throws exception");
//        }catch(InterruptedException e){
//            /**这样处理不好
//             * System.out.println("catch interrupted exception");
//             * e.printStackTrace();
//             */
//             Thread.currentThread().interrupt();//这样处理比较好
//        }
//    }
//}
//这样，就由 生吞异常 变成了 将 异常事件 进一步扩散了。
//
//不可取消的任务
//有些任务拒绝被中断，这使得它们是不可取消的。但是，即使是不可取消的任务也应该尝试保留中断状态，以防在不可取消的任务结束之后，调用栈上更
//高层的代码需要对中断进行处理。以下代码展示了一个方法，该方法等待一个阻塞队列，直到队列中出现一个可用项目，而不管它是否被中断。为了方便
//他人，它在结束后在一个 finally 块中恢复中断状态，以免剥夺中断请求的调用者的权利。
//
//public Task getNextTask(BlockingQueue<Task> queue) {
// 
//    boolean interrupted = false;
// 
//    try {
//        while (true) {
//            try {
//                return queue.take();
//            } catch (InterruptedException e) {
//                interrupted = true;
//                // fall through and retry
//            }
//        }
//    } finally {
//        if (interrupted)
//            Thread.currentThread().interrupt();
//    }
// 
//}
//如何安全的终止线程
//安全的终止线程，除了中断外，还可以利用一个boolean变量来控制是否需要停止任务并终止线程。
//
//class CanCancelThread extends Thread {
// 
//    private long i;
//    private volatile boolean on = true;
// 
//    @Override
//    public void run() {
//        while (on && !Thread.currentThread().isInterrupted()) {
//            i++;
//        }
//        System.out.println("Count i=" + i);
//    }
// 
//    public void cancel() {
//        on = false;
//    }
//}
//可以通过标识位或者中断的方式都能够是线程终止时有机会去清理资源，因此这种方式显得更加优雅和安全。
